<template>
  <div class="ef-flow-box" v-if="easyFlowVisible">
    <div class="ef-flow-header">
      <el-row>
        <!--顶部工具菜单-->
        <el-col :span="24">
          <div class="ef-tooltar">
            <el-link type="primary" :underline="false">{{ flowData.name }}</el-link>
            <el-divider direction="vertical"></el-divider>
            <!--顶部工具菜单放大功能-->
            <!--<el-tooltip class="item" effect="dark" :open-delay="300" :hide-after="3000" content="点击放大" placement="bottom">-->
            <!--<el-button type="text" icon="el-icon-plus" size="large" @click="zoomAdd"></el-button>-->
            <!--</el-tooltip>-->
            <!--<el-divider direction="vertical"></el-divider>-->
            <!--<el-tooltip class="item" effect="dark" content="点击缩小" placement="bottom">-->
            <!--<el-button type="text" icon="el-icon-minus" size="large" @click="zoomSub"></el-button>-->
            <!--</el-tooltip>-->
            <el-tooltip class="item" effect="dark" :open-delay="300" content="保存流程图" placement="bottom">
              <el-button type="text" icon="el-icon-receiving" size="large" @click="saveFlow"></el-button>
            </el-tooltip>
            <!--<el-divider direction="vertical"></el-divider>-->
            <!--<el-tooltip class="item" effect="dark" :open-delay="300" content="生产流程图片" placement="bottom">-->
            <!--<el-button type="text" icon="el-icon-picture" size="large" @click="downloadData"></el-button>-->
            <!--</el-tooltip>-->
            <el-divider direction="vertical"></el-divider>
            <el-tooltip class="item" effect="dark" content="重新绘制" placement="bottom">
              <el-button type="text" icon="el-icon-refresh" size="large" @click="clearFlow"></el-button>
            </el-tooltip>

            <el-divider direction="vertical"></el-divider>
            <el-tooltip class="item" effect="dark" content="点击删除" placement="bottom">
              <el-button type="text" icon="el-icon-delete" size="large" @click="deleteFlow"
                         :disabled="!this.activeElement.type"
              ></el-button>
            </el-tooltip>
            <el-divider direction="vertical"></el-divider>

            <el-tooltip class="item" effect="dark" content="设置" placement="bottom">
              <el-button type="text" icon="el-icon-setting" size="large"
                         @click="settingFlow"
              ></el-button>
            </el-tooltip>
          </div>
        </el-col>
      </el-row>
    </div>

    <div class="ef-flow-main">
      <transition name="moveL">
        <div class="ef-left-box" v-show="isLeftMenu">
          <node-menu @addNode="addNode" ref="nodeMenu"></node-menu>
          <div class="ef-left-switch" @click="isLeftBox">
            <i class="el-icon-caret-left"></i>
          </div>
        </div>
      </transition>
      <div class="ef-flow-content" ref="efContainer" v-flowDrag @click.stop="containerHandler">
        <template v-for="node in flowData.nodeList">
          <flow-node
            :id="node.id"
            :key="node.id"
            :node="node"
            :activeElement="activeElement"
            @changeNodeSite="changeNodeSite"
            @nodeRightMenu="nodeRightMenu"
            @clickNode="clickNode"
          >
          </flow-node>
        </template>
        <!-- 给画布一个默认的宽度和高度 -->
        <div class="ef-flow-canvas">&nbsp;</div>
        <div class="ef-content-switch" @click="isFlowCtl" v-show="isShowIcon">
          <i class="el-icon-caret-right"></i>
        </div>
      </div>
      <!-- 右侧表单 -->
      <transition name="moveR">
        <div class="ef-right-box" v-show="isShowR">
          <flow-node-form ref="nodeForm" @setLineLabel="setLineLabel"
                          @repaintEverything="repaintEverything"
          ></flow-node-form>
        </div>
      </transition>
    </div>
  </div>

</template>

<script>
import draggable from 'vuedraggable'
import { jsPlumb } from 'jsplumb'
import './style/index.css'
// 使用修改后的jsplumb
import { easyFlowMixin } from './config/mixins'//设置流程图参数
import flowNode from './modules/node'//流程图内节点框的编辑
import nodeMenu from './modules/node_menu'//左边tab
import FlowNodeForm from './modules/node_form'//右边tab
import lodash from 'lodash'//JavaScript 实用工具库

export default {
  data() {
    return {
      isShowIcon: false,
      isLeftMenu: true,
      isShowR: false,
      // jsPlumb 实例
      jsPlumb: null,
      // 控制画布销毁
      easyFlowVisible: true,
      // 是否加载完毕标志位
      loadEasyFlowFinish: false,
      // 数据
      flowData: {},
      // 激活的元素、可能是节点、可能是连线
      activeElement: {
        // 可选值 node 、line
        type: undefined,
        // 节点ID
        nodeId: undefined,
        // 连线ID
        sourceId: undefined,
        targetId: undefined
      },
      zoom: 0.5
    }
  },
  // 一些基础配置移动该文件中
  mixins: [easyFlowMixin],
  components: {
    draggable, flowNode, nodeMenu, FlowNodeForm
  },
  //自定义指令 https://www.jb51.net/article/108047.htm
  directives: {
    'flowDrag': {
      bind(el, binding, vnode, oldNode) {
        if (!binding) {
          return
        }
        el.onmousedown = (e) => {
          if (e.button == 2) {
            // 右键不管
            return
          }
          //  鼠标按下，计算当前原始距离可视区的高度
          let disX = e.clientX
          let disY = e.clientY
          el.style.cursor = 'move'

          document.onmousemove = function(e) {
            // 移动时禁止默认事件
            e.preventDefault()
            const left = e.clientX - disX
            disX = e.clientX
            el.scrollLeft += -left

            const top = e.clientY - disY
            disY = e.clientY
            el.scrollTop += -top
          }

          document.onmouseup = function(e) {
            el.style.cursor = 'auto'
            document.onmousemove = null
            document.onmouseup = null
          }
        }
      }
    }
  },
  mounted() {
    this.jsPlumb = jsPlumb.getInstance()
    this.$nextTick(() => {
      // 默认加载流程A的数据、在这里可以根据具体的业务返回符合流程数据格式的数据即可
      const data_test = {
        name: '测试',
        nodeList: [
          {
            id: 'nodeA',
            name: '节点A-不可拖拽',
            type: 'task',
            left: '18px',
            top: '223px',
            ico: 'el-icon-user-solid',
            state: 'success',
            viewOnly: true
          },
          {
            id: 'nodeB',
            type: 'task',
            name: '流程B-节点B',
            left: '351px',
            top: '96px',
            ico: 'el-icon-goods',
            state: 'error'
          },
          {
            id: 'nodeC',
            name: '流程B-节点C',
            type: 'task',
            left: '354px',
            top: '351px',
            ico: 'el-icon-present',
            state: 'warning'
          }, {
            id: 'nodeD',
            name: '流程B-节点D',
            type: 'task',
            left: '723px',
            top: '215px',
            ico: 'el-icon-present',
            state: 'running'
          }
        ],
        lineList: [{
          from: 'nodeA',
          to: 'nodeB',
          label: '条件A'
        }, {
          from: 'nodeA',
          to: 'nodeC',
          label: '条件B'
        }, {
          from: 'nodeB',
          to: 'nodeD'
        }, {
          from: 'nodeC',
          to: 'nodeD'
        }
        ]
      }
      // console.log(localStorage.getItem('data_test'),'---');
      // const data_test = localStorage.getItem('data_test')
      console.log('初始化流程数据', data_test)
      this.dataReload(data_test)
    })
  },
  methods: {
    containerHandler(event) {
      if (event.currentTarget === event.target) {
        console.log('containerHandler')
        this.isShowR = false
      }
    },
    // 返回唯一标识
    getUUID() {
      return new Date().getTime() + Math.random().toString(36).substr(3, 10)
    },
    jsPlumbInit() {
      this.jsPlumb.ready(() => {
        // 导入默认配置
        this.jsPlumb.importDefaults(this.jsplumbSetting)
        // 会使整个jsPlumb立即重绘。
        this.jsPlumb.setSuspendDrawing(false, true)
        // 初始化节点
        this.loadEasyFlow()
        // 单点击了连接线, https://www.cnblogs.com/ysx215/p/7615677.html
        this.jsPlumb.bind('click', (conn, originalEvent) => {
          console.log('单点击了连接线', conn)
          this.activeElement.type = 'line'
          this.activeElement.sourceId = conn.sourceId
          this.activeElement.targetId = conn.targetId
          this.$refs.nodeForm.lineInit({
            from: conn.sourceId,
            to: conn.targetId,
            label: conn.getLabel()
          })
        })
        // 连线
        this.jsPlumb.bind('connection', (evt) => {
          let from = evt.source.id
          let to = evt.target.id
          if (this.loadEasyFlowFinish) {
            this.flowData.lineList.push({ from: from, to: to })
          }
        })

        // 删除连线回调
        this.jsPlumb.bind('connectionDetached', (evt) => {
          this.deleteLine(evt.sourceId, evt.targetId)
        })

        // 改变线的连接节点
        this.jsPlumb.bind('connectionMoved', (evt) => {
          this.changeLine(evt.originalSourceId, evt.originalTargetId)
        })

        // 连线右击
        this.jsPlumb.bind('contextmenu', (evt) => {
          console.log('连线右击', evt)
        })

        // 连线
        this.jsPlumb.bind('beforeDrop', (evt) => {
          console.log('连接开始', evt)
          let from = evt.sourceId
          let to = evt.targetId
          if (from === to) {
            this.$message.error('节点不支持连接自己')
            return false
          }
          if (this.hasLine(from, to)) {
            this.$message.error('该关系已存在,不允许重复创建')
            return false
          }
          if (this.hashOppositeLine(from, to)) {
            this.$message.error('不支持两个节点之间连线回环')
            return false
          }
          this.$message.success('连接成功')
          return true
        })

        // beforeDetach
        this.jsPlumb.bind('beforeDetach', (evt) => {
          console.log('beforeDetach', evt)
        })
        this.jsPlumb.setContainer(this.$refs.efContainer)
      })
    },
    // 加载流程图
    loadEasyFlow() {
      // 初始化节点
      for (var i = 0; i < this.flowData.nodeList.length; i++) {
        let node = this.flowData.nodeList[i]
        // 设置源点，可以拖出线连接其他节点
        this.jsPlumb.makeSource(node.id, lodash.merge(this.jsplumbSourceOptions, {}))
        // // 设置目标点，其他源点拖出的线可以连接该节点
        this.jsPlumb.makeTarget(node.id, this.jsplumbTargetOptions)
        if (!node.viewOnly) {
          this.jsPlumb.draggable(node.id, {
            containment: 'parent',
            stop: function(el) {
              // 拖拽节点结束后的对调
              console.log('拖拽结束001: ', el)
            }
          })
        }
      }
      // 初始化连线
      for (var i = 0; i < this.flowData.lineList.length; i++) {
        let line = this.flowData.lineList[i]
        var connParam = {
          source: line.from,
          target: line.to,
          label: line.label ? line.label : '',
          connector: line.connector ? line.connector : '',
          anchors: line.anchors ? line.anchors : undefined,
          paintStyle: line.paintStyle ? line.paintStyle : undefined
        }
        this.jsPlumb.connect(connParam, this.jsplumbConnectOptions)
      }
      this.$nextTick(function() {
        this.loadEasyFlowFinish = true
      })
    },
    // 设置连线条件
    setLineLabel(from, to, label) {
      var conn = this.jsPlumb.getConnections({
        source: from,
        target: to
      })[0]
      if (!label || label === '') {
        conn.removeClass('flowLabel')
        conn.addClass('emptyFlowLabel')
      } else {
        conn.addClass('flowLabel')
      }
      conn.setLabel({
        label: label
      })
      this.flowData.lineList.forEach(function(line) {
        if (line.from == from && line.to == to) {
          line.label = label
        }
      })

    },
    isLeftBox() {
      let that = this
      that.isLeftMenu = !that.isLeftMenu
      setTimeout(function() {
        that.isShowIcon = true
      }, 300)
    },
    isFlowCtl() {
      console.log(999)
      this.isLeftMenu = !this.isLeftMenu
      this.isShowIcon = false
    },
    //设置
    settingFlow() {
      console.log('按钮给我测试其他的功能--1', this.isLeftMenu)

    },
    // 删除激活的元素
    deleteFlow() {
      if (this.activeElement.type === 'node') {
        this.deleteNode(this.activeElement.nodeId)
      } else if (this.activeElement.type === 'line') {
        this.$confirm('确定删除所点击的线吗?', '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(() => {
          var conn = this.jsPlumb.getConnections({
            source: this.activeElement.sourceId,
            target: this.activeElement.targetId
          })[0]
          this.jsPlumb.deleteConnection(conn)
        }).catch(() => {
        })
      }
    },
    // 删除线
    deleteLine(from, to) {
      this.flowData.lineList = this.flowData.lineList.filter(function(line) {
        if (line.from == from && line.to == to) {
          return false
        }
        return true
      })
    },
    // 改变连线
    changeLine(oldFrom, oldTo) {
      this.deleteLine(oldFrom, oldTo)
    },
    // 改变节点的位置
    changeNodeSite(flowData) {
      console.log(666)
      for (var i = 0; i < this.flowData.nodeList.length; i++) {
        let node = this.flowData.nodeList[i]
        if (node.id === flowData.nodeId) {
          node.left = flowData.left
          node.top = flowData.top
        }
      }
    },
    /**
     * 拖拽结束后添加新的节点
     * @param evt
     * @param nodeMenu 被添加的节点对象
     * @param mousePosition 鼠标拖拽结束的坐标
     */
    addNode(evt, nodeMenu, mousePosition) {
      var screenX = evt.originalEvent.clientX, screenY = evt.originalEvent.clientY
      let efContainer = this.$refs.efContainer
      var containerRect = efContainer.getBoundingClientRect()
      var left = screenX, top = screenY
      // 计算是否拖入到容器中
      if (left < containerRect.x || left > containerRect.width + containerRect.x || top < containerRect.y || containerRect.y > containerRect.y + containerRect.height) {
        this.$message.error('请把节点拖入到画布中')
        return
      }
      left = left - containerRect.x + efContainer.scrollLeft
      top = top - containerRect.y + efContainer.scrollTop
      // 居中
      left -= 85
      top -= 16
      var nodeId = this.getUUID()
      // 动态生成名字
      var origName = nodeMenu.name
      var nodeName = origName
      var index = 1
      while (index < 10000) {
        var repeat = false
        for (var i = 0; i < this.flowData.nodeList.length; i++) {
          let node = this.flowData.nodeList[i]
          if (node.name === nodeName) {
            nodeName = origName + index
            repeat = true
          }
        }
        if (repeat) {
          index++
          continue
        }
        break
      }
      console.log('-------003', nodeMenu.type)
      var node = {
        id: nodeId, //节点id--前端唯一的
        name: nodeName, //节点名称
        type: nodeMenu.type, //节点类型
        left: left + 'px', //坐标
        top: top + 'px', //坐标
        ico: nodeMenu.ico //图标
      }
      if (nodeMenu.type === 'channel') {
        node = {
          id: nodeId, //节点id--前端唯一的
          name: nodeName, //节点名称
          type: nodeMenu.type, //节点类型
          left: left + 'px', //坐标
          top: top + 'px', //坐标
          ico: nodeMenu.ico, //图标
          channel: 'channel01', //渠道名称
          clientAccounted: '',  //渠道 客户占比
          clientDistribution: true, //渠道 客户是否随机分配
          strategyOrCustom: ' '  //渠道 选择分组策略 or 自定义 ***弹窗回显
        }
      } else if (nodeMenu.type === 'customerFission') {
        node = {
          id: nodeId, //节点id--前端唯一的
          name: nodeName, //节点名称
          type: nodeMenu.type, //节点类型
          left: left + 'px', //坐标
          top: top + 'px', //坐标
          ico: nodeMenu.ico, //图标
          groupingCustomer: '', //客群 分组客群标识
          customerDistribution: true, //客群 客户是否随机分配
          customerAccounted: '', //客群 客户占比
          customerOrCustom: '' //客群 ***弹窗回显
        }
      } else if (nodeMenu.type === 'intelligentRecommend') {
        node = {
          id: nodeId, //节点id--前端唯一的
          name: nodeName, //节点名称
          type: nodeMenu.type, //节点类型
          left: left + 'px', //坐标
          top: top + 'px', //坐标
          ico: nodeMenu.ico, //图标
          marketingProduct: '', //智能推荐 营销产品 ***弹窗回显
          testProduct: '', //智能推荐 测试产品 ***弹窗回显
          productDistribution: true, //智能推荐 客户是否随机分配
          productAccounted: '', //智能推荐 客户占比
          productOrIntelligent: '智能推荐测试一下' //智能推荐 ***弹窗回显
        }
      } else if (nodeMenu.type === 'handmadeRecommend') {
        node = {
          id: nodeId, //节点id--前端唯一的
          name: nodeName, //节点名称
          type: nodeMenu.type, //节点类型
          left: left + 'px', //坐标
          top: top + 'px', //坐标
          ico: nodeMenu.ico, //图标
          handmadeMarketingProduct: '', //手工推荐 营销产品 ***弹窗回显
          handmadeTestProduct: '', //手工推荐 测试产品 ***弹窗回显
          handmadeProductDistribution: true, //手工推荐 客户是否随机分配
          handmadeProductAccounted: '', //手工推荐 客户占比
          productOrHandmade: '手工推荐测试一下' //手工推荐 ***弹窗回显
        }
      } else if (nodeMenu.type === 'timeQuantum') {
        node = {
          id: nodeId, //节点id--前端唯一的
          name: nodeName, //节点名称
          type: nodeMenu.type, //节点类型
          left: left + 'px', //坐标
          top: top + 'px', //坐标
          ico: nodeMenu.ico, //图标
          startTime: '', //时间段- 开始时间段
          endTime: '', //时间段-  结束时间段
          timeDistribution: true, //时间段- 客户是否随机分配
          timeAccounted: '', //时间段- 客户占比
          timeOrHandmade: '时间段-测试一下' // 时间段- ***弹窗回显
        }
      }
      /**
       * 这里可以进行业务判断、是否能够添加该节点
       */
      this.flowData.nodeList.push(node)
      this.$nextTick(function() {
        this.jsPlumb.makeSource(nodeId, this.jsplumbSourceOptions)
        this.jsPlumb.makeTarget(nodeId, this.jsplumbTargetOptions)
        this.jsPlumb.draggable(nodeId, {
          containment: 'parent',
          stop: function(el) {
            // 拖拽节点结束后的对调
            console.log('拖拽结束002: ', el)
          }
        })
      })
    },
    /**
     * 删除节点
     * @param nodeId 被删除节点的ID
     */
    deleteNode(nodeId) {
      this.$confirm('确定要删除节点' + nodeId + '?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
        closeOnClickModal: false
      }).then(() => {
        /**
         * 这里需要进行业务判断，是否可以删除
         */
        this.flowData.nodeList = this.flowData.nodeList.filter(function(node) {
          if (node.id === nodeId) {
            // 伪删除，将节点隐藏，否则会导致位置错位
            // node.show = false
            return false
          }
          return true
        })
        this.$nextTick(function() {
          this.jsPlumb.removeAllEndpoints(nodeId)
        })
      }).catch(() => {
      })
      return true
    },
    clickNode(nodeId) {
      console.log('点击节点操作', nodeId)
      this.isShowR = true
      this.activeElement.type = 'node'
      this.activeElement.nodeId = nodeId
      this.$refs.nodeForm.nodeInit(this.flowData, nodeId)
    },
    // 是否具有该线
    hasLine(from, to) {
      for (var i = 0; i < this.flowData.lineList.length; i++) {
        var line = this.flowData.lineList[i]
        if (line.from === from && line.to === to) {
          return true
        }
      }
      return false
    },
    // 是否含有相反的线
    hashOppositeLine(from, to) {
      return this.hasLine(to, from)
    },
    nodeRightMenu(nodeId, evt) {
      console.log(88)
      this.menu.show = true
      this.menu.curNodeId = nodeId
      this.menu.left = evt.x + 'px'
      this.menu.top = evt.y + 'px'
    },
    repaintEverything() {
      this.jsPlumb.repaint()
    },
    // 加载流程图-不能删
    dataReload(flowData) {
      this.easyFlowVisible = false
      this.flowData.nodeList = []
      this.flowData.lineList = []
      this.$nextTick(() => {
        flowData = lodash.cloneDeep(flowData)
        this.easyFlowVisible = true
        this.flowData = flowData
        // console.log(flowData, '渲染数据');
        this.$nextTick(() => {
          this.jsPlumb = jsPlumb.getInstance()
          this.$nextTick(() => {
            this.jsPlumbInit()
          })
        })
      })
    },
    // 保存流程图
    saveFlow() {
      const that = this
      let flowObj = Object.assign({}, that.flowData)
      // localStorage.setItem("data_test", JSON.stringify(flowObj))
      console.log('保存流程图', flowObj)
      // if (!that.checkFlow()) return;
      that.$message.success('保存流程成功！暂时请查先看控制台。')
    },
    //重绘流程图
    clearFlow() {
      let that = this

      console.log('重绘流程图--1', that.flowData)
      that.$confirm('确定要重新绘制流程图吗？', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
        closeOnClickModal: false
      }).then(() => {
        if (that.flowData.nodeList.length !== 0) {
          that.flowData.nodeList.forEach(function(node, index) {
            that.jsPlumb.remove(node.id)
          })
          that.flowData.nodeList = []
          that.flowData.linkList = []
          that.$message.success('设置成功')
          console.log('重绘流程图--002', that.flowData)
        }
      }).catch(() => {
        console.log('取消重绘流程图--1')
        that.$message.error('取消重制')
      })

    },
    //放小
    zoomSub() {
      let that = this
      if (that.zoom <= 0) {
        return
      }
      // setInterval(() => {
      that.zoom = that.zoom - 0.1
      that.$refs.efContainer.style.transform = `scale(${that.zoom})`
      that.jsPlumb.setZoom(that.zoom)
      // }, 1000)
    },
    //放大
    zoomAdd() {
      let that = this
      if (that.zoom >= 1) {
        return
      }
      // setInterval(() => {
      that.zoom = that.zoom + 0.1

      // that.$refs.efContainer.style.transform = `scale(${that.zoom})`
      console.log(that.zoom)
      that.$refs.efContainer.style.transform = `scale(${that.zoom})`
      that.jsPlumb.setZoom(that.zoom)
      // }, 1000)
    },
    // 下载数据
    downloadData() {
      this.$confirm('确定要下载该流程数据吗？', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
        closeOnClickModal: false
      }).then(() => {
        var datastr = 'data:text/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(this.flowData, null, '\t'))
        var downloadAnchorNode = document.createElement('a')
        downloadAnchorNode.setAttribute('href', datastr)
        downloadAnchorNode.setAttribute('download', 'flowData.json')
        downloadAnchorNode.click()
        downloadAnchorNode.remove()
        this.$message.success('正在下载中,请稍后...')
      }).catch(() => {
      })
    }
  }
}
</script>
<style lang="scss" type="text/scss">
.moveR-enter-active, .moveR-leave-active {
  transition: all 0.3s linear;
  transform: translateX(0);
}

.moveR-enter, .moveR-leave {
  transform: translateX(100%);
}

.moveR-leave-to {
  transform: translateX(100%);
}

.moveL-enter-active, .moveL-leave-active {
  transition: all 0.3s linear;
  transform: translateX(0%);
}

.moveL-enter,
.moveL-leave {
  transform: translateX(-100%);
}

.moveL-leave-to {
  transform: translateX(-100%);
}
</style>
